package jmine.tec.batch.domain;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;

import jmine.tec.batch.domain.enumx.PhaseStatusEnum;
import jmine.tec.persist.Versioned;
import jmine.tec.persist.annotation.Alias;
import jmine.tec.persist.annotation.Comment;
import jmine.tec.persist.annotation.Constraint;
import jmine.tec.persist.annotation.DiscriminatorComment;
import jmine.tec.persist.annotation.Index;
import jmine.tec.persist.schema.api.IndexOrder;

import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.Type;

import bancosys.tec.component.Documentation;
import bancosys.tec.persist.Persistable;
import bancosys.tec.persist.bussobj.PersistableBusinessObject;
import bancosys.tec.utils.date.Date;
import bancosys.tec.utils.date.Timestamp;

/**
 * Bean que armazena o estado da execução de uma fase para um elemento.
 * <p>
 * Um elemento é o generics para um objeto de negócio do domínio da aplicação, sobre o qual a fase atua.
 * 
 * @param <E> o elemento sobre o qual a fase atua.
 */
@Entity
@Table(name = "PHASE_INSTANCE")
@Alias("PHINST")
@SequenceGenerator(name = "SEQ_PHINST", sequenceName = "SEQ_PHINST")
@DiscriminatorValue(value = "GENERICO")
@DiscriminatorComment("FASE GENERICA")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING)
@Constraint(suffix = "0")
@Comment(table = "PHASE_INSTANCE", column = "DISCRIMINATOR", value = "IDENTIFICADOR DO ESTADO DA EXECUCAO DE UMA FASE.")
@Documentation("ARMAZENA O ESTADO DA EXECUCAO DE UMA FASE DO PROCESSAMENTO.")
public class PhaseInstance<E extends Persistable & Versioned> extends PersistableBusinessObject {

    private Long id;

    private MPhase phase;

    private Date data;

    private PhaseStatusEnum status = PhaseStatusEnum.OPENED;

    // Dados armazenados sobre o ULTIMO processamento
    private Process process;

    private Timestamp startTime;

    private Timestamp finishTime;

    /**
     * @return o valor do id.
     */
    @Id
    @Column(name = "COD_PHASE_INSTANCE")
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_PHINST")
    @Documentation("CODIGO DO ESTADO DA EXECUCAO DE UMA FASE.")
    public Long getId() {
        return this.id;
    }

    /**
     * Seta o id.
     * 
     * @param id o valor do id.
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * A fase dessa instância.
     * 
     * @return a fase.
     */
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "COD_MPHASE", updatable = false, nullable = false)
    @ForeignKey(name = "FK_MPHASE_PHINST")
    @Documentation("CODIGO DA FASE.")
    @Index(columns = "COD_MPHASE", suffix = "0", orders = IndexOrder.ASC)
    public MPhase getPhase() {
        return this.phase;
    }

    /**
     * Seta a fase dessa instância.
     * 
     * @param phase o valor da fase.
     */
    public void setPhase(MPhase phase) {
        this.phase = phase;
    }

    /**
     * Informa a data de refererência da instância da fase.
     * 
     * @return a data de referência.
     */
    @Column(name = "DT_EXEC", nullable = false, updatable = false)
    @Type(type = "bancosys.tec.persist.hibernate.type.DateType")
    @Documentation("DATA DA EXECUCAO DO PROCESSO.")
    public Date getData() {
        return this.data;
    }

    /**
     * Seta a data de referência da instância da fase.
     * 
     * @param data o valor da data de referência da instância da fase.
     */
    public void setData(Date data) {
        this.data = data;
    }

    /**
     * @return O status dessa fase.
     */
    @Column(name = "STATUS", nullable = false)
    @Documentation("INDICA O ESTADO DA EXECUCAO DO PROCESSO, QUE PODE SER: NAO INICIADO (0), INICIADO (1),"
            + "FINALIZADO COM ERROS (2), FINALIZADO SEM ERROS (3).")
    @Constraint(suffix = "1")
    public PhaseStatusEnum getStatus() {
        return this.status;
    }

    /**
     * O valor do status da fase.
     * 
     * @param status o valor do status da fase.
     */
    public void setStatus(PhaseStatusEnum status) {
        this.status = status;
    }

    /**
     * @return valor do campo process.
     */
    @Index(suffix = "JT_0")
    @ManyToOne
    @JoinColumn(name = "COD_PROCESS")
    @Documentation("CODIGO DO PROCESSO EXECUTADO.")
    public Process getProcess() {
        return this.process;
    }

    /**
     * Define valor para campo process.
     * 
     * @param process valor para campo process.
     */
    public void setProcess(Process process) {
        this.process = process;
    }

    /**
     * @return valor do campo startTime.
     */
    @Index(suffix = "JT_1")
    @Type(type = "bancosys.tec.persist.hibernate.type.TimestampType")
    @Column(name = "TS_START")
    @Documentation("HORARIO INICIAL DE EXECUCAO DE UMA FASE.")
    public Timestamp getStartTime() {
        return this.startTime;
    }

    /**
     * Define valor para campo startTime.
     * 
     * @param startTime valor para campo startTime.
     */
    public void setStartTime(Timestamp startTime) {
        this.startTime = startTime;
    }

    /**
     * @return valor do campo finishTime.
     */
    @Type(type = "bancosys.tec.persist.hibernate.type.TimestampType")
    @Column(name = "TS_FINISH")
    @Documentation("HORARIO FINAL DE EXECUCAO DE UMA FASE.")
    public Timestamp getFinishTime() {
        return this.finishTime;
    }

    /**
     * Define valor para campo finishTime.
     * 
     * @param finishTime valor para campo finishTime.
     */
    public void setFinishTime(Timestamp finishTime) {
        this.finishTime = finishTime;
    }

    /**
     * Retorna o elemento sobre as quais as fases {@link MPhase} dessa instância foram executados. <br>
     * 
     * @return o elemento que a instância guarda o estado.
     */
    @Transient
    public E getElement() {
        throw new UnsupportedOperationException("Esta operação deve ser sobrecarregada");
    }
}
